/*
 *  Copyright 2013 Utkin Dmitry
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include <string>
#include <QTcpSocket>
#include <QHostAddress>
#include <QtEndian>
#include <QDebug>
#include "protoqtrpc/rpcmessage.pb.h"
#include "SocketRpcProtocol.h"
#include "SocketRpcServerSession.h"
#include "SocketRpcServer.h"

namespace protoqtrpc
{

SocketRpcServerSession::SocketRpcServerSession(QTcpSocket* sock, SocketRpcServer* server):
    RpcServerSession(server), mSock(sock), mRpcProtocol(new SocketRpcProtocol(sock))
{
#ifdef QT_DEBUG
    qDebug() << "Received new connection from "
             << sock->peerAddress().toString() << ":" << sock->peerPort();
#endif
    qRegisterMetaType<protoqtrpc::RpcMessage>("protoqtrpc::RpcMessage");

    mPeerInfo.address = mSock->peerAddress().toIPv4Address();
    mPeerInfo.port = sock->peerPort();

    emit connected(mPeerInfo);
    connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this,
            SLOT(socketError(QAbstractSocket::SocketError)), Qt::DirectConnection);

    connect(this, SIGNAL(sendMessage(protoqtrpc::RpcMessage)),
            mRpcProtocol, SLOT(sendMessage(protoqtrpc::RpcMessage)));
    connect(mRpcProtocol, SIGNAL(messageReceived(protoqtrpc::RpcMessage)),
            this, SLOT(handleMessage(protoqtrpc::RpcMessage)));

    // move this session into separate thread with own event loop
    moveToThread(&mThread);
    mThread.start();
}

SocketRpcServerSession::~SocketRpcServerSession()
{
    if (mSock->state() == QAbstractSocket::UnconnectedState)
    {
        mSock->abort();
    }
    else
    {
        mSock->disconnectFromHost();
        mSock->waitForDisconnected(-1);
        if (mSock->isOpen())
            mSock->close();
        emit disconnected(mPeerInfo);
    }

    mThread.quit();
    mThread.wait();

    delete mRpcProtocol;
    mSock->deleteLater();
}

const PeerInfo& SocketRpcServerSession::peerInfo()
{
    return mPeerInfo;
}

void SocketRpcServerSession::socketError(QAbstractSocket::SocketError err)
{
    if (err == QAbstractSocket::RemoteHostClosedError)
    {
        qDebug() << "Connection to " << mSock->peerAddress().toString()
                 << " closed by client";
        emit disconnected(mPeerInfo);
    }
    else
    {
        qWarning() << "Error " << err << ": " << mSock->errorString();
    }
}

}
